package com.spider.spiderrule.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.spider.spiderrule.enums.RuleDataStatusEnum;
import com.spider.spiderrule.enums.RuleHeaderTypeEnum;
import com.spider.spiderrule.exception.BusinessException;
import com.spider.spiderrule.model.dto.RuleRedisConditionDto;
import com.spider.spiderrule.model.entity.SpiderRule;
import com.spider.spiderrule.model.vo.RulePublishHeaderVo;
import com.spider.spiderrule.model.vo.RulePublishItemVo;
import com.spider.spiderrule.model.vo.RuleRedisConditionVo;
import com.spider.spiderrule.service.SpiderRuleMatchApiService;
import com.spider.spiderrule.service.SpiderRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xiong.bo
 * @version 1.0
 * @date 2024/4/21 11:04 上午
 */

@Slf4j
@Service
public class SpiderRuleMatchApiServiceImpl implements SpiderRuleMatchApiService {

    @Autowired
    private SpiderRuleService spiderRuleService;

    @Override
    public JSONObject matchRuleByRuleCode(String ruleCode, JSONObject parameter) {

        if (!StringUtils.hasText(ruleCode)) {
            throw new BusinessException("请传入规则编码");
        }
        SpiderRule spiderRule = spiderRuleService.findByRuleCode(ruleCode);
        if (RuleDataStatusEnum.PUBLISH.getCode() != spiderRule.getDataStatus()) {
            throw new BusinessException(String.format("规则未发布,ruleCode:【%s】", ruleCode));
        }

        RuleRedisConditionVo redisConditionVo =
                spiderRuleService.findRulePublishFromRedis(ruleCode, RuleHeaderTypeEnum.CONDITION.getName(), spiderRule.getTenantId());
        log.info("redisConditionVo:{}", JSON.toJSONString(redisConditionVo));

        RuleRedisConditionVo redisResultVo =
                spiderRuleService.findRulePublishFromRedis(ruleCode, RuleHeaderTypeEnum.RESULT.getName(), spiderRule.getTenantId());
        log.info("redisResultVo:{}", JSON.toJSONString(redisConditionVo));

        if (redisConditionVo == null || redisResultVo == null) {
            throw new BusinessException("未存在规则发布数据，请检查规则是否已发布");
        }

        // 封装便于匹配的规则矩阵数据集合
        List<RuleRedisConditionDto> conditionDtoList = assembleConditinList(redisConditionVo);

        // 遍历匹配入参和规则矩阵数据
        Integer rowIndex = matchRuleSequenceSingle(conditionDtoList, parameter);
        log.info("matchRuleSequenceSingle rowIndex:{}", rowIndex);

        // 封装返回结果
        JSONObject matchResult = assembleMatchResult(redisResultVo, rowIndex);
        matchResult.put("ruleCode", ruleCode);

        return matchResult;
    }


    /**
     * 封装便于匹配的规则矩阵数据集合
     */
    private List<RuleRedisConditionDto> assembleConditinList(RuleRedisConditionVo redisConditionVo) {

        // 规则矩阵数据的List集合
        List<RuleRedisConditionDto> conditionResultList = new ArrayList<>();

        List<RulePublishHeaderVo> conditionVoList = redisConditionVo.getConditionVoList();
        Map<String, List<RulePublishItemVo>> conditionVoListMap = redisConditionVo.getConditionVoListMap();

        // 获取所有的规则矩阵表行数据
        List<RulePublishItemVo> allItemList = new ArrayList<>();
        for (Map.Entry<String, List<RulePublishItemVo>> entry : conditionVoListMap.entrySet()) {
            allItemList.addAll(entry.getValue());
        }

        // 排序
        allItemList = allItemList.stream().sorted(Comparator.comparingInt(RulePublishItemVo::getRowIndex))
                .collect(Collectors.toList());

        // 循环遍历组装RuleRedisConditionDto对象
        Integer rowIndexFlg = -1;
        for (RulePublishItemVo itemVo : allItemList) {

            RuleRedisConditionDto conditionDto;
            if (rowIndexFlg.equals(itemVo.getRowIndex()) && conditionResultList.size() > 0) {
                conditionDto = conditionResultList.get(conditionResultList.size() - 1);
            } else {
                //rowIndex开始增加
                rowIndexFlg = itemVo.getRowIndex();
                conditionDto = new RuleRedisConditionDto();
                conditionResultList.add(conditionDto);
            }
            conditionDto.setHeaderList(conditionVoList);
            for (RulePublishHeaderVo headerVo : conditionVoList) {
                if (itemVo.getHeaderUuid().equals(headerVo.getHeaderUuid())) {
                    Map<String, RulePublishItemVo> itemVoMap = conditionDto.getItemVoMap();
                    if (itemVoMap == null) {
                        itemVoMap = new HashMap<>();
                        conditionDto.setItemVoMap(itemVoMap);
                    }
                    itemVoMap.put(headerVo.getHeaderUuid(), itemVo);
                    break;
                }
            }
            conditionDto.setRowIndex(itemVo.getRowIndex());
        }

        return conditionResultList;
    }

    /**
     * 匹配规则：
     * 从上到下顺序匹配，满足条件后立即返回第一条规则矩阵数据的行数
     */
    private Integer matchRuleSequenceSingle(List<RuleRedisConditionDto> conditionDtoList, JSONObject parameter) {

        Integer result = 0;
        if (parameter == null) {
            return result;
        }

        for (RuleRedisConditionDto conditionDto : conditionDtoList) {

            List<RulePublishHeaderVo> headerList = conditionDto.getHeaderList();
            Map<String, RulePublishItemVo> itemVoMap = conditionDto.getItemVoMap();
            if (headerList == null || itemVoMap == null) {
                break;
            }

            int headerCount = headerList.size();
            int trueCount = 0;

            for (RulePublishHeaderVo headerVo : headerList) {
                String parameterValue = parameter.getString(headerVo.getFieldCode());
                RulePublishItemVo itemVo = itemVoMap.get(headerVo.getHeaderUuid());
                if (itemVo == null) {
                    // todo 这里当某个输入的规则矩阵没有配置，先默认成功，后续看怎么定义
                    trueCount++;
                    break;
                }

                String operator = itemVo.getOperator();
                if (!StringUtils.hasText(operator)) {
                    trueCount++;
                    break;
                }
                JSONObject operatorJson = JSON.parseObject(operator);
                if (operatorJson == null) {
                    trueCount++;
                    break;
                }
                String operatorType = operatorJson.getString("operator");
                String operatorValue = operatorJson.getString("value");

                if (!StringUtils.hasText(operatorValue)) {
                    trueCount++;
                    break;
                }

                boolean itemMatchFlag = false;//单个规则矩阵的匹配结果的标识

                if (StringUtils.hasText(operatorValue) && !StringUtils.hasText(parameterValue)) {
                    // 匹配不通过
                    break;
                }

                if ("contain".equals(operatorType)) {
                    itemMatchFlag = parameterValue.contains(operatorValue);
                } else if ("equal".equals(operatorType)) {
                    itemMatchFlag = parameterValue.equals(operatorValue);
                }
                if (itemMatchFlag) {
                    trueCount++;
                }
            }
            if (headerCount == trueCount) {
                result = conditionDto.getRowIndex();
                break;
            }
        }
        return result;
    }

    /**
     * 封装规则匹配结果
     */
    private JSONObject assembleMatchResult(RuleRedisConditionVo redisResultVo, Integer rowIndex) {

        JSONObject ruleResultJson = new JSONObject();

        List<RulePublishHeaderVo> conditionVoList = redisResultVo.getConditionVoList();
        Map<String, List<RulePublishItemVo>> conditionVoListMap = redisResultVo.getConditionVoListMap();
        for (RulePublishHeaderVo headerVo : conditionVoList) {
            List<RulePublishItemVo> itemVoList = conditionVoListMap.get(headerVo.getHeaderUuid());
            Map<String, RulePublishItemVo> rowIndexMap = itemVoList.stream().
                    collect(Collectors.toMap(e -> String.valueOf(e.getRowIndex()), t -> t, (t1, t2) -> t1));
            RulePublishItemVo itemVo = rowIndexMap.get(String.valueOf(rowIndex));

            // 拿item的operator中的value
            String operatorValue = "";
            String operator = itemVo.getOperator();
            if (StringUtils.hasText(operator)) {
                JSONObject operatorJson = JSON.parseObject(operator);
                if (operatorJson != null) {
                    operatorValue = operatorJson.getString("value");
                }
            }
            ruleResultJson.put(headerVo.getFieldCode(), operatorValue);
        }
        ruleResultJson.put("rowIndex", rowIndex);

        return ruleResultJson;
    }

}
